page.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. "use client";
  2. import {fetchApi} from "@/app/_modules/func";
  3. import {DeleteOutlined, ExclamationCircleFilled, PlusOutlined, ReloadOutlined,} from "@ant-design/icons";
  4. import type {ActionType, ProColumns, ProFormInstance,} from "@ant-design/pro-components";
  5. import {PageContainer, ProTable,} from "@ant-design/pro-components";
  6. import type {GetProp, UploadProps} from "antd";
  7. import {Button, message, Modal, Space, Tag, Upload,} from "antd";
  8. import {useRouter} from "next/navigation";
  9. import {faCheck, faToggleOff, faToggleOn, faXmark,} from "@fortawesome/free-solid-svg-icons";
  10. import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
  11. import {useRef, useState} from "react";
  12. type FileType = Parameters<GetProp<UploadProps, "beforeUpload">>[0];
  13. const { Dragger } = Upload;
  14. export type OptionType = {
  15. label: string;
  16. value: string | number;
  17. };
  18. export default function RoleAuth({ params }: { params: { roleid: string } }) {
  19. const { push } = useRouter();
  20. const roleId = params.roleid;
  21. // 添加用于控制批量取消授权确认模态框的状态
  22. const [batchRemoveAuthModalVisible, setBatchRemoveAuthModalVisible] = useState(false);
  23. // 添加用于控制单个取消授权确认模态框的状态
  24. const [removeAuthModalVisible, setRemoveAuthModalVisible] = useState(false);
  25. const [removeAuthRecord, setRemoveAuthRecord] = useState<any>(null);
  26. //表格列定义
  27. const columns: ProColumns[] = [
  28. {
  29. title: "用户名称",
  30. dataIndex: "userName",
  31. order: 2,
  32. },
  33. {
  34. title: "用户昵称",
  35. dataIndex: "nickName",
  36. search: false,
  37. },
  38. {
  39. title: "邮箱",
  40. dataIndex: "email",
  41. search: false,
  42. },
  43. {
  44. title: "手机号",
  45. dataIndex: "phonenumber",
  46. order: 1,
  47. },
  48. {
  49. title: "状态",
  50. dataIndex: "status",
  51. search: false,
  52. valueEnum: {
  53. 0: {
  54. text: "正常",
  55. status: "0",
  56. },
  57. 1: {
  58. text: "停用",
  59. status: "1",
  60. },
  61. },
  62. render: (text, record) => {
  63. return (
  64. <Space>
  65. <Tag
  66. color={record.status == 0 ? "green" : "red"}
  67. icon={
  68. record.status == 0 ? (
  69. <FontAwesomeIcon icon={faCheck} />
  70. ) : (
  71. <FontAwesomeIcon icon={faXmark} />
  72. )
  73. }
  74. >
  75. {text}
  76. </Tag>
  77. </Space>
  78. );
  79. },
  80. },
  81. {
  82. title: "创建时间",
  83. dataIndex: "createTime",
  84. valueType: "dateTime",
  85. search: false,
  86. },
  87. {
  88. title: "操作",
  89. key: "option",
  90. search: false,
  91. render: (_, record) => {
  92. if (record.userId != 1)
  93. return [
  94. <Button
  95. key="deleteBtn"
  96. type="link"
  97. danger
  98. icon={<DeleteOutlined />}
  99. onClick={() => onClickRemoveAuth(record)}
  100. >
  101. 取消授权
  102. </Button>,
  103. ];
  104. },
  105. },
  106. ];
  107. //未分配授权用户列定义
  108. const unAllocateColumns: ProColumns[] = [
  109. {
  110. title: "用户名称",
  111. dataIndex: "userName",
  112. order: 2,
  113. },
  114. {
  115. title: "用户昵称",
  116. dataIndex: "nickName",
  117. search: false,
  118. },
  119. {
  120. title: "邮箱",
  121. dataIndex: "email",
  122. search: false,
  123. },
  124. {
  125. title: "手机号",
  126. dataIndex: "phonenumber",
  127. order: 1,
  128. },
  129. {
  130. title: "状态",
  131. dataIndex: "status",
  132. search: false,
  133. valueEnum: {
  134. 0: {
  135. text: "正常",
  136. status: "0",
  137. },
  138. 1: {
  139. text: "停用",
  140. status: "1",
  141. },
  142. },
  143. render: (text, record) => {
  144. return (
  145. <Space>
  146. <Tag
  147. color={record.status == 0 ? "green" : "red"}
  148. icon={
  149. record.status == 0 ? (
  150. <FontAwesomeIcon icon={faCheck} />
  151. ) : (
  152. <FontAwesomeIcon icon={faXmark} />
  153. )
  154. }
  155. >
  156. {text}
  157. </Tag>
  158. </Space>
  159. );
  160. },
  161. },
  162. {
  163. title: "创建时间",
  164. dataIndex: "createTime",
  165. valueType: "dateTime",
  166. search: false,
  167. },
  168. ];
  169. //查询角色授权数据
  170. const getRoleAllocate = async (params: any, sorter: any, filter: any) => {
  171. const searchParams = {
  172. roleId: roleId,
  173. pageNum: params.current,
  174. ...params,
  175. };
  176. delete searchParams.current;
  177. const queryParams = new URLSearchParams(searchParams);
  178. Object.keys(sorter).forEach((key) => {
  179. queryParams.append("orderByColumn", key);
  180. if (sorter[key] === "ascend") {
  181. queryParams.append("isAsc", "ascending");
  182. } else {
  183. queryParams.append("isAsc", "descending");
  184. }
  185. });
  186. const body = await fetchApi(
  187. `/api/system/role/authUser/allocatedList?${queryParams}`,
  188. push
  189. );
  190. if (body !== undefined) {
  191. return body;
  192. }
  193. };
  194. //查询角色未授权数据
  195. const getRoleUnallocate = async (params: any, sorter: any, filter: any) => {
  196. const searchParams = {
  197. roleId: roleId,
  198. pageNum: params.current,
  199. ...params,
  200. };
  201. delete searchParams.current;
  202. const queryParams = new URLSearchParams(searchParams);
  203. Object.keys(sorter).forEach((key) => {
  204. queryParams.append("orderByColumn", key);
  205. if (sorter[key] === "ascend") {
  206. queryParams.append("isAsc", "ascending");
  207. } else {
  208. queryParams.append("isAsc", "descending");
  209. }
  210. });
  211. const body = await fetchApi(
  212. `/api/system/role/authUser/unallocatedList?${queryParams}`,
  213. push
  214. );
  215. if (body !== undefined) {
  216. return body;
  217. }
  218. };
  219. //取消授权按钮是否可用,选中行时才可用
  220. const [rowCanRemoveAuth, setCanRemoveAuth] = useState(false);
  221. //点击批量取消授权按钮
  222. const onClickBatchRemoveAuth = () => {
  223. setBatchRemoveAuthModalVisible(true);
  224. };
  225. //执行批量取消用户角色授权
  226. const executeBatchRemoveRoleAuth = async () => {
  227. const data = {
  228. roleId: roleId,
  229. userIds: selectedRowKeys.join(","),
  230. };
  231. const body = await fetchApi(
  232. `/api/system/role/authUser/cancelAll?${new URLSearchParams(data)}`,
  233. push,
  234. {
  235. method: "PUT",
  236. }
  237. );
  238. if (body !== undefined) {
  239. if (body.code == 200) {
  240. message.success("批量取消授权成功");
  241. } else {
  242. message.error(body.msg);
  243. }
  244. setSelectedRowKeys([]);
  245. //刷新表格
  246. if (actionRef.current) {
  247. actionRef.current.reload();
  248. }
  249. }
  250. setBatchRemoveAuthModalVisible(false);
  251. };
  252. //点击取消授权按钮
  253. const onClickRemoveAuth = (record: any) => {
  254. setRemoveAuthRecord(record);
  255. setRemoveAuthModalVisible(true);
  256. };
  257. //执行取消用户角色授权
  258. const executeRemoveRoleAuth = async () => {
  259. if (!removeAuthRecord) return;
  260. const data = {
  261. roleId: roleId,
  262. userId: removeAuthRecord.userId,
  263. };
  264. const body = await fetchApi("/api/system/role/authUser/cancel", push, {
  265. method: "PUT",
  266. headers: {
  267. "Content-Type": "application/json",
  268. },
  269. body: JSON.stringify(data),
  270. });
  271. if (body !== undefined) {
  272. if (body.code == 200) {
  273. message.success("取消授权成功");
  274. } else {
  275. message.error(body.msg);
  276. }
  277. //刷新表格
  278. if (actionRef.current) {
  279. actionRef.current.reload();
  280. }
  281. }
  282. setRemoveAuthModalVisible(false);
  283. setRemoveAuthRecord(null);
  284. };
  285. //取消单个取消授权操作
  286. const cancelRemoveAuth = () => {
  287. setRemoveAuthModalVisible(false);
  288. setRemoveAuthRecord(null);
  289. };
  290. //选中行操作
  291. const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  292. const rowSelection = {
  293. onChange: (newSelectedRowKeys: React.Key[]) => {
  294. setSelectedRowKeys(newSelectedRowKeys);
  295. setCanRemoveAuth(newSelectedRowKeys && newSelectedRowKeys.length > 0);
  296. },
  297. };
  298. //未授权用户选中行操作
  299. const [selectedRowKeysUnallocate, setSelectedRowKeysUnallocate] = useState<
  300. React.Key[]
  301. >([]);
  302. const rowSelectionUnallocate = {
  303. onChange: (newSelectedRowKeys: React.Key[]) => {
  304. setSelectedRowKeysUnallocate(newSelectedRowKeys);
  305. },
  306. };
  307. //是否展示分配用户对话框
  308. const [showUnallocateModal, setShowUnallocateModal] = useState(false);
  309. //展示分配用户对话框
  310. const onClickShowModal = () => {
  311. if (unallocateActionRef.current) {
  312. unallocateActionRef.current.reload();
  313. }
  314. setShowUnallocateModal(true);
  315. };
  316. //确认分配新的用户
  317. const confirmAddUnallocate = async () => {
  318. const data = {
  319. roleId: roleId,
  320. userIds: selectedRowKeysUnallocate.join(","),
  321. };
  322. const body = await fetchApi(
  323. `/api/system/role/authUser/selectAll?${new URLSearchParams(data)}`,
  324. push,
  325. {
  326. method: "PUT",
  327. }
  328. );
  329. if (body !== undefined) {
  330. if (body.code == 200) {
  331. message.success(body.msg);
  332. } else {
  333. message.error(body.msg);
  334. }
  335. }
  336. setSelectedRowKeysUnallocate([]);
  337. if (unallocateActionRef.current) {
  338. unallocateActionRef.current.reload();
  339. }
  340. console.log(selectedRowKeysUnallocate);
  341. if (actionRef.current) {
  342. actionRef.current.reload();
  343. }
  344. setShowUnallocateModal(false);
  345. };
  346. //取消分配用户
  347. const cancelAddUnallocate = () => {
  348. setShowUnallocateModal(false);
  349. };
  350. //搜索栏显示状态
  351. const [showSearch, setShowSearch] = useState(true);
  352. //action对象引用
  353. const actionRef = useRef<ActionType>(null);
  354. //表单对象引用
  355. const formRef = useRef<ProFormInstance>(null!);
  356. //未分配用户列表action对象引用
  357. const unallocateActionRef = useRef<ActionType>(null);
  358. //当前默认条数
  359. const defaultPageSize = 10;
  360. return (
  361. <PageContainer
  362. header={{
  363. title: "分配用户",
  364. onBack(e) {
  365. push("/system/role");
  366. },
  367. }}
  368. >
  369. <ProTable
  370. formRef={formRef}
  371. rowKey="userId"
  372. rowSelection={{
  373. selectedRowKeys,
  374. ...rowSelection,
  375. }}
  376. columns={columns}
  377. request={async (params: any, sorter: any, filter: any) => {
  378. // 表单搜索项会从 params 传入,传递给后端接口。
  379. const data = await getRoleAllocate(params, sorter, filter);
  380. if (data !== undefined) {
  381. return Promise.resolve({
  382. data: data.rows,
  383. success: true,
  384. total: data.total,
  385. });
  386. }
  387. return Promise.resolve({
  388. data: [],
  389. success: true,
  390. });
  391. }}
  392. pagination={{
  393. defaultPageSize: defaultPageSize,
  394. showQuickJumper: true,
  395. showSizeChanger: true,
  396. }}
  397. search={
  398. showSearch
  399. ? {
  400. defaultCollapsed: false,
  401. searchText: "搜索",
  402. }
  403. : false
  404. }
  405. dateFormatter="string"
  406. actionRef={actionRef}
  407. toolbar={{
  408. actions: [
  409. <Button icon={<PlusOutlined />} key="allocate" type="primary" onClick={onClickShowModal}>
  410. 添加用户
  411. </Button>,
  412. <Button
  413. key="unallocate"
  414. danger
  415. icon={<DeleteOutlined />}
  416. disabled={!rowCanRemoveAuth}
  417. onClick={() => onClickBatchRemoveAuth()}
  418. >
  419. 批量取消授权
  420. </Button>,
  421. ],
  422. settings: [
  423. {
  424. key: "switch",
  425. icon: showSearch ? (
  426. <FontAwesomeIcon icon={faToggleOn} />
  427. ) : (
  428. <FontAwesomeIcon icon={faToggleOff} />
  429. ),
  430. tooltip: showSearch ? "隐藏搜索栏" : "显示搜索栏",
  431. onClick: (key: string | undefined) => {
  432. setShowSearch(!showSearch);
  433. },
  434. },
  435. {
  436. key: "refresh",
  437. tooltip: "刷新",
  438. icon: <ReloadOutlined />,
  439. onClick: (key: string | undefined) => {
  440. if (actionRef.current) {
  441. actionRef.current.reload();
  442. }
  443. },
  444. },
  445. ],
  446. }}
  447. />
  448. <Modal
  449. title={`选择用户`}
  450. width={1000}
  451. open={showUnallocateModal}
  452. onOk={confirmAddUnallocate}
  453. onCancel={cancelAddUnallocate}
  454. >
  455. <ProTable
  456. rowKey="userId"
  457. rowSelection={{
  458. selectedRowKeys: selectedRowKeysUnallocate,
  459. ...rowSelectionUnallocate,
  460. }}
  461. columns={unAllocateColumns}
  462. request={async (params: any, sorter: any, filter: any) => {
  463. // 表单搜索项会从 params 传入,传递给后端接口。
  464. const data = await getRoleUnallocate(params, sorter, filter);
  465. if (data !== undefined) {
  466. return Promise.resolve({
  467. data: data.rows,
  468. success: true,
  469. total: data.total,
  470. });
  471. }
  472. return Promise.resolve({
  473. data: [],
  474. success: true,
  475. });
  476. }}
  477. pagination={{
  478. defaultPageSize: defaultPageSize,
  479. showQuickJumper: true,
  480. showSizeChanger: true,
  481. }}
  482. search={
  483. showSearch
  484. ? {
  485. defaultCollapsed: false,
  486. searchText: "搜索",
  487. }
  488. : false
  489. }
  490. dateFormatter="string"
  491. actionRef={unallocateActionRef}
  492. toolbar={{
  493. actions: [],
  494. settings: [],
  495. }}
  496. />
  497. </Modal>
  498. {/* 批量取消授权确认模态框 */}
  499. <Modal
  500. title={
  501. <div style={{ display: 'flex', alignItems: 'center' }}>
  502. <ExclamationCircleFilled style={{ color: '#faad14', marginRight: 8 }} />
  503. <span>系统提示</span>
  504. </div>
  505. }
  506. open={batchRemoveAuthModalVisible}
  507. onOk={executeBatchRemoveRoleAuth}
  508. onCancel={() => setBatchRemoveAuthModalVisible(false)}
  509. okText="确认"
  510. cancelText="取消"
  511. >
  512. <p>确定要取消选中用户的角色授权吗?</p>
  513. </Modal>
  514. {/* 单个取消授权确认模态框 */}
  515. <Modal
  516. title={
  517. <div style={{ display: 'flex', alignItems: 'center' }}>
  518. <ExclamationCircleFilled style={{ color: '#faad14', marginRight: 8 }} />
  519. <span>系统提示</span>
  520. </div>
  521. }
  522. open={removeAuthModalVisible}
  523. onOk={executeRemoveRoleAuth}
  524. onCancel={cancelRemoveAuth}
  525. okText="确认"
  526. cancelText="取消"
  527. >
  528. <p>{`确定要取消用户“${removeAuthRecord?.userName}”的角色授权吗?`}</p>
  529. </Modal>
  530. </PageContainer>
  531. );
  532. }